import os
import sqlite3
import time
import threading
from pathlib import Path
from typing import List, Dict, Set, Optional, Tuple
import fnmatch
from datetime import datetime
from collections import deque


class InteractiveFileIndexer:
    def __init__(self, db_path: str = "benchmark.db"):
        self.db_path = db_path
        self.conn = None
        self.batch_size = 100000
        self.batch_data = []
        self.page_size = 10
        self.search_history = []  # 存储搜索历史
        self.history_index = -1
        self.init_database()

    def init_database(self):
        """初始化数据库"""
        start_time = time.time()
        self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
        self.conn.execute('PRAGMA journal_mode = WAL')
        self.conn.execute('PRAGMA synchronous = NORMAL')
        self.conn.execute('PRAGMA cache_size = -100000')

        cursor = self.conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS files (
                id INTEGER PRIMARY KEY,
                path TEXT UNIQUE,
                name TEXT,
                extension TEXT,
                size INTEGER,
                modified REAL,
                is_dir INTEGER
            )
        ''')
        self.conn.commit()
        end_time = time.time()
        print(f"✓ 数据库初始化完成 (耗时: {end_time - start_time:.3f}s)")

    def update_index(self, directories: List[str] = None, exclude_patterns: List[str] = None) -> Tuple[int, int]:
        """更新文件索引"""
        if directories is None:
            # 使用更安全的目录路径
            possible_dirs = [
                os.path.expanduser("c:\\"),
                os.path.expanduser("d:\\"),
                os.path.expanduser("e:\\"),
            ]
            # 只选择存在的目录
            directories = [d for d in possible_dirs if os.path.exists(d)]

            if not directories:
                print("⚠️  没有找到可索引的目录，使用当前目录")
                directories = [os.getcwd()]

        if exclude_patterns is None:
            exclude_patterns = [
            ]

        print(f"\n🔄 开始更新文件索引...")
        total_start = time.time()

        # 获取现有文件数量
        cursor = self.conn.cursor()
        cursor.execute('SELECT COUNT(*) FROM files')
        old_count = cursor.fetchone()[0]

        # 清空现有数据
        clear_start = time.time()
        cursor.execute('DELETE FROM files')
        self.conn.commit()
        clear_time = time.time() - clear_start

        total_files = 0
        total_dirs = 0

        index_start = time.time()
        for directory in directories:
            if not os.path.exists(directory):
                print(f"⚠️  警告: 目录不存在: {directory}")
                continue

            print(f"📁 正在索引: {directory}")
            dir_start = time.time()

            try:
                # 使用BFS遍历
                queue = deque([directory])

                while queue:
                    current_dir = queue.popleft()

                    try:
                        with os.scandir(current_dir) as entries:
                            for entry in entries:
                                try:
                                    if exclude_patterns and self._should_exclude(entry.path, exclude_patterns):
                                        continue

                                    stat = entry.stat()
                                    extension = os.path.splitext(entry.name)[1].lower() if entry.is_file() else ""

                                    self.batch_data.append((
                                        entry.path,
                                        entry.name,
                                        extension,
                                        stat.st_size,
                                        stat.st_mtime,
                                        1 if entry.is_dir() else 0
                                    ))

                                    if entry.is_dir():
                                        total_dirs += 1
                                        queue.append(entry.path)
                                    else:
                                        total_files += 1

                                    if len(self.batch_data) >= self.batch_size:
                                        self._commit_batch()

                                except (OSError, PermissionError) as e:
                                    continue
                    except (OSError, PermissionError) as e:
                        continue
                self._commit_batch()
                dir_time = time.time() - dir_start
                print(f"   ✅ 完成 ({dir_time:.2f}s)")

            except Exception as e:
                print(f"❌ 索引目录 {directory} 时出错: {e}")
                continue

        # 提交剩余的批处理数据
        if self.batch_data:
            self._commit_batch()

        index_time = time.time() - index_start

        # 创建索引
        index_create_start = time.time()
        self._create_indexes()
        index_create_time = time.time() - index_create_start

        total_time = time.time() - total_start

        # 获取新文件数量
        cursor.execute('SELECT COUNT(*) FROM files')
        new_count = cursor.fetchone()[0]

        print(f"\n📊 索引更新完成!")
        print(f"   ⏱️  总耗时: {total_time:.2f}s")
        print(f"   📂 文件数: {total_files}")
        print(f"   📁 目录数: {total_dirs}")
        print(f"   🗃️  总记录: {new_count}")
        print(f"   🗑️  清空耗时: {clear_time:.2f}s")
        print(f"   🔍 索引耗时: {index_time:.2f}s")
        print(f"   🏷️  索引创建: {index_create_time:.2f}s")

        return total_files, total_dirs

    def _should_exclude(self, path: str, patterns: List[str]) -> bool:
        """检查路径是否应该被排除"""
        normalized_path = path.replace('\\', '/')

        for pattern in patterns:
            normalized_pattern = pattern.replace('\\', '/')
            if fnmatch.fnmatch(normalized_path, normalized_pattern):
                return True
            if fnmatch.fnmatch(os.path.basename(normalized_path), normalized_pattern):
                return True

        return False

    def _commit_batch(self):
        """提交批处理数据到数据库"""
        if not self.batch_data:
            return

        try:
            # cursor = self.conn.cursor()
            # cursor.executemany('''
            #     INSERT INTO files (path, name, extension, size, modified, is_dir)
            #     VALUES (?, ?, ?, ?, ?, ?)
            # ''', self.batch_data)
            # self.conn.commit()
            print(f"✓ 已提交 {len(self.batch_data)} 条记录")
            self.batch_data = []
        except Exception as e:
            print(f"❌ 提交批处理时出错: {e}")
            self.conn.rollback()

    def _create_indexes(self):
        """创建索引"""
        try:
            cursor = self.conn.cursor()
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_name ON files(name)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_extension ON files(extension)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_modified ON files(modified)')
            self.conn.commit()
            print("✓ 索引创建完成")
        except Exception as e:
            print(f"❌ 创建索引时出错: {e}")

    def search_files(self, query: str, file_types: List[str] = None, page: int = 1) -> Tuple[List[Dict], int, float]:
        """搜索文件并分页显示"""
        search_start = time.time()

        cursor = self.conn.cursor()

        # 构建查询
        conditions = ["name LIKE ?"]
        params = [f"%{query}%"]

        if file_types:
            placeholders = ','.join(['?' for _ in file_types])
            conditions.append(f"extension IN ({placeholders})")
            params.extend(file_types)

        where_clause = " AND ".join(conditions)

        # 先获取总数
        count_sql = f'SELECT COUNT(*) FROM files WHERE {where_clause}'
        cursor.execute(count_sql, params)
        total_results = cursor.fetchone()[0]

        if total_results == 0:
            return [], 0, 0

        # 计算分页
        total_pages = (total_results + self.page_size - 1) // self.page_size
        page = max(1, min(page, total_pages))
        offset = (page - 1) * self.page_size

        # 获取当前页数据
        sql = f'''
            SELECT path, name, extension, size, modified, is_dir 
            FROM files 
            WHERE {where_clause}
            ORDER BY 
                CASE 
                    WHEN name LIKE ? THEN 0
                    WHEN name LIKE ? THEN 1
                    ELSE 2
                END,
                modified DESC
            LIMIT ? OFFSET ?
        '''

        search_params = params + [f"{query}%", f"{query}%", self.page_size, offset]

        cursor.execute(sql, search_params)
        results = cursor.fetchall()

        files = []
        for row in results:
            files.append({
                'path': row[0],
                'name': row[1],
                'extension': row[2],
                'size': self._format_size(row[3]),
                'modified': datetime.fromtimestamp(row[4]).strftime("%Y-%m-%d %H:%M"),
                'is_dir': bool(row[5])
            })

        search_time = time.time() - search_start

        return files, total_pages, search_time

    def _format_size(self, size_bytes: int) -> str:
        """格式化文件大小"""
        if size_bytes == 0:
            return "0B"

        size_names = ["B", "KB", "MB", "GB"]
        i = 0
        while size_bytes >= 1024 and i < len(size_names) - 1:
            size_bytes /= 1024.0
            i += 1

        return f"{size_bytes:.1f}{size_names[i]}"

    def get_stats(self) -> Dict:
        """获取统计信息"""
        cursor = self.conn.cursor()

        cursor.execute('SELECT COUNT(*) FROM files WHERE is_dir = 0')
        file_count = cursor.fetchone()[0]

        cursor.execute('SELECT COUNT(*) FROM files WHERE is_dir = 1')
        dir_count = cursor.fetchone()[0]

        cursor.execute('SELECT COUNT(DISTINCT extension) FROM files WHERE is_dir = 0')
        ext_count = cursor.fetchone()[0]

        cursor.execute('SELECT MAX(modified) FROM files')
        latest_modified = cursor.fetchone()[0]
        latest_time = datetime.fromtimestamp(latest_modified).strftime("%Y-%m-%d %H:%M") if latest_modified else "N/A"

        return {
            'total_files': file_count,
            'total_dirs': dir_count,
            'unique_extensions': ext_count,
            'latest_update': latest_time
        }

    def _get_user_input(self, prompt: str) -> str:
        """获取用户输入（简化版，没有readline）"""
        try:
            return input(prompt).strip()
        except (KeyboardInterrupt, EOFError):
            return "/exit"

    def interactive_search(self):
        """交互式搜索界面"""
        print("\n" + "=" * 60)
        print("🔍 文件搜索系统 - 交互模式")
        print("=" * 60)
        print("命令说明:")
        print("  /help     - 显示帮助信息")
        print("  /stats    - 显示统计信息")
        print("  /exit     - 退出程序")
        print("  /page N   - 跳转到第N页")
        print("  /type ext - 按文件类型搜索 (如: /type pdf)")
        print("  /clear    - 清除类型筛选")
        print("  搜索词    - 搜索文件")
        print("=" * 60)

        current_page = 1
        current_query = ""
        current_file_types = None

        while True:
            try:
                user_input = self._get_user_input("\n🔍 请输入搜索词或命令: ")

                if not user_input:
                    continue

                # 处理命令
                if user_input.startswith('/'):
                    command = user_input[1:].lower().split()

                    if not command:
                        continue

                    if command[0] == 'exit':
                        print("👋 再见!")
                        break

                    elif command[0] == 'help':
                        print("\n📖 帮助信息:")
                        print("  /help     - 显示此帮助")
                        print("  /stats    - 显示索引统计")
                        print("  /exit     - 退出程序")
                        print("  /page N   - 跳转到指定页码")
                        print("  /type ext - 按文件类型筛选")
                        print("  /clear    - 清除类型筛选")
                        print("  直接输入文字进行搜索")

                    elif command[0] == 'stats':
                        stats = self.get_stats()
                        print(f"\n📊 索引统计:")
                        print(f"  文件数量: {stats['total_files']}")
                        print(f"  目录数量: {stats['total_dirs']}")
                        print(f"  文件类型: {stats['unique_extensions']} 种")
                        print(f"  最后更新: {stats['latest_update']}")

                    elif command[0] == 'page' and len(command) > 1:
                        try:
                            new_page = int(command[1])
                            current_page = new_page
                            print(f"📄 跳转到第 {current_page} 页")
                            if current_query:
                                self._display_results(current_query, current_file_types, current_page)
                        except ValueError:
                            print("❌ 页码必须是数字")

                    elif command[0] == 'type' and len(command) > 1:
                        file_type = command[1].lower()
                        if not file_type.startswith('.'):
                            file_type = '.' + file_type
                        current_file_types = [file_type]
                        print(f"📄 筛选文件类型: {file_type}")
                        if current_query:
                            current_page = 1
                            self._display_results(current_query, current_file_types, current_page)

                    elif command[0] == 'clear':
                        current_file_types = None
                        print("✅ 已清除类型筛选")
                        if current_query:
                            current_page = 1
                            self._display_results(current_query, current_file_types, current_page)

                    else:
                        print("❌ 未知命令，输入 /help 查看帮助")

                else:
                    # 处理搜索
                    current_query = user_input
                    current_page = 1
                    # 添加到搜索历史
                    if user_input not in self.search_history:
                        self.search_history.append(user_input)
                    self._display_results(current_query, current_file_types, current_page)

            except KeyboardInterrupt:
                print("\n👋 再见!")
                break
            except Exception as e:
                print(f"❌ 发生错误: {e}")

    def _display_results(self, query: str, file_types: List[str], page: int):
        """显示搜索结果"""
        print(f"\n🔍 搜索: '{query}'", end="")
        if file_types:
            print(f" [类型: {', '.join(file_types)}]", end="")
        print()

        start_time = time.time()
        results, total_pages, search_time = self.search_files(query, file_types, page)
        display_time = time.time() - start_time

        if not results:
            print("❌ 没有找到匹配的文件")
            return

        print(
            f"📄 第 {page}/{total_pages} 页 (共 {len(results)} 条结果, 搜索耗时: {search_time:.3f}s, 显示耗时: {display_time:.3f}s)")
        print("-" * 80)

        for i, file in enumerate(results, 1):
            icon = "📁" if file['is_dir'] else "📄"
            print(f"{icon} {(page - 1) * self.page_size + i:2d}. {file['name']}")
            print(f"   📍 路径: {file['path']}")
            if not file['is_dir']:
                print(f"   📦 大小: {file['size']} | 🕒 修改: {file['modified']}")
            print()

        print("-" * 80)
        if total_pages > 1:
            print(f"📄 使用 /page [页码] 切换页面 (1-{total_pages})")

    def close(self):
        """关闭数据库连接"""
        if self.conn:
            self.conn.close()


def main():
    """主函数"""
    print("🚀 启动文件索引系统...")

    # 初始化索引器
    indexer = InteractiveFileIndexer()

    # 更新索引
    try:
        print("🔄 正在更新索引，请稍候...")
        files, dirs = indexer.update_index()

        if files == 0 and dirs == 0:
            print("⚠️  没有索引到任何文件，请检查目录路径")
            print("💡 提示: 可以修改代码中的 directories 列表来指定要索引的目录")

    except Exception as e:
        print(f"❌ 索引更新失败: {e}")
        print("💡 提示: 可能是权限问题，请尝试以管理员身份运行")
        return

    # 进入交互模式
    indexer.interactive_search()

    # 关闭连接
    indexer.close()


if __name__ == "__main__":
    main()